package com.suny.dingd_demo.config;

import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.domain.values.StatusInfo;
import de.codecentric.boot.admin.server.notify.AbstractStatusChangeNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import reactor.core.publisher.Mono;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Arrays;

@Slf4j
@Component
public class DingTalkNotifierConfig extends AbstractStatusChangeNotifier {
    
    @Value("${spring.boot.admin.notify.dingtalk.webhook-url}")
    private String webhookUrl;
    
    @Value("${spring.boot.admin.notify.dingtalk.secret}")
    private String secret;
    
    private final RestTemplate restTemplate;

    public DingTalkNotifierConfig(InstanceRepository repository) {
        super(repository);
        this.restTemplate = new RestTemplate();
    }

    @Override
    protected boolean shouldNotify(InstanceEvent event, Instance instance) {
        if (event instanceof InstanceStatusChangedEvent) {
            InstanceStatusChangedEvent statusChangeEvent = (InstanceStatusChangedEvent) event;
            String status = statusChangeEvent.getStatusInfo().getStatus();
            log.info("Status change event received: {}", event);
            return true;
        }
        return false;
    }

    @Override
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> {
            try {
                if (webhookUrl == null || webhookUrl.isEmpty()) {
                    log.error("Webhook URL is not configured!");
                    return;
                }

                if (!(event instanceof InstanceStatusChangedEvent)) {
                    log.debug("Not a status change event, ignoring...");
                    return;
                }

                InstanceStatusChangedEvent statusChangeEvent = (InstanceStatusChangedEvent) event;
                String serviceName = instance.getRegistration().getName();
                String serviceUrl = instance.getRegistration().getServiceUrl();
                StatusInfo statusInfo = statusChangeEvent.getStatusInfo();
                String previousStatus = getPreviousStatus(event);  // 获取之前的状态
                String currentStatus = statusInfo.getStatus();
                
                // 构建消息内容
                StringBuilder messageBuilder = new StringBuilder();
                messageBuilder.append("## ").append(serviceName).append("服务状态变更通知\n\n");
                
                // 基本信息部分
                messageBuilder.append("### 基本信息\n");
                messageBuilder.append("> - 服务名称: ").append(serviceName).append("\n");
                messageBuilder.append("> - 服务地址: ").append(serviceUrl).append("\n");
                messageBuilder.append("> - 实例ID: ").append(instance.getId()).append("\n");
                messageBuilder.append("> - 变更前状态: ").append(getStatusEmoji(previousStatus)).append("\n");
                messageBuilder.append("> - 当前状态: ").append(getStatusEmoji(currentStatus)).append("\n");
                messageBuilder.append("> - 变更时间: ").append(event.getTimestamp()).append("\n\n");

                // 系统信息部分
                Map<String, Object> details = statusInfo.getDetails();
                if (details != null && !details.isEmpty()) {
                    messageBuilder.append("### 系统信息\n");
                    
                    // 磁盘信息
                    if (details.containsKey("diskSpace")) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> diskSpace = (Map<String, Object>) details.get("diskSpace");
                        if (diskSpace != null && diskSpace.containsKey("details")) {
                            @SuppressWarnings("unchecked")
                            Map<String, Object> diskDetails = (Map<String, Object>) diskSpace.get("details");
                            if (diskDetails != null) {
                                Long total = (Long) diskDetails.get("total");
                                Long free = (Long) diskDetails.get("free");
                                if (total != null && free != null) {
                                    messageBuilder.append("> - 磁盘空间: ").append(formatBytes(free))
                                                .append("/").append(formatBytes(total))
                                                .append(" (已使用: ").append(String.format("%.1f", (1 - free.doubleValue()/total) * 100)).append("%)\n");
                                }
                            }
                        }
                    }

                    // 服务发现信息
                    if (details.containsKey("discoveryComposite")) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> discovery = (Map<String, Object>) details.get("discoveryComposite");
                        if (discovery != null && discovery.containsKey("components")) {
                            @SuppressWarnings("unchecked")
                            Map<String, Object> components = (Map<String, Object>) discovery.get("components");
                            if (components != null && components.containsKey("discoveryClient")) {
                                @SuppressWarnings("unchecked")
                                Map<String, Object> client = (Map<String, Object>) components.get("discoveryClient");
                                if (client != null && client.containsKey("details")) {
                                    @SuppressWarnings("unchecked")
                                    Map<String, Object> details2 = (Map<String, Object>) client.get("details");
                                    if (details2 != null && details2.containsKey("services")) {
                                        @SuppressWarnings("unchecked")
                                        List<String> services = (List<String>) details2.get("services");
                                        messageBuilder.append("> - 已注册服务: ").append(String.join(", ", services)).append("\n");
                                    }
                                }
                            }
                        }
                    }

                    // Nacos配置状态
                    if (details.containsKey("nacosConfig")) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> nacos = (Map<String, Object>) details.get("nacosConfig");
                        if (nacos != null) {
                            messageBuilder.append("> - Nacos配置: ").append(nacos.get("status")).append("\n");
                        }
                    }

                    // Nacos服务发现状态
                    if (details.containsKey("nacosDiscovery")) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> nacos = (Map<String, Object>) details.get("nacosDiscovery");
                        if (nacos != null) {
                            messageBuilder.append("> - Nacos服务发现: ").append(nacos.get("status")).append("\n");
                        }
                    }

                    // 其他组件状态
                    Arrays.asList("refreshScope", "ping").forEach(component -> {
                        if (details.containsKey(component)) {
                            @SuppressWarnings("unchecked")
                            Map<String, Object> componentDetails = (Map<String, Object>) details.get(component);
                            if (componentDetails != null) {
                                String componentName = component.equals("refreshScope") ? "配置刷新" : "健康检查";
                                messageBuilder.append("> - ").append(componentName).append(": ")
                                            .append(componentDetails.get("status")).append("\n");
                            }
                        }
                    });

                    messageBuilder.append("\n");
                }

                // 错误详情（如果有）
                if (details != null && details.containsKey("error")) {
                    messageBuilder.append("### 错误详情\n");
                    messageBuilder.append("> ").append(details.get("error")).append("\n\n");
                }

                // 操作建议
                messageBuilder.append("### 操作建议\n");
                messageBuilder.append(getActionSuggestion(currentStatus));

                // 发送消息到钉钉
                Map<String, Object> message = new HashMap<>();
                message.put("msgtype", "markdown");
                
                Map<String, String> markdown = new HashMap<>();
                markdown.put("title", serviceName + "服务状态变更");
                markdown.put("text", messageBuilder.toString());
                message.put("markdown", markdown);

                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                HttpEntity<Map<String, Object>> request = new HttpEntity<>(message, headers);
                
                String finalUrl = addSignature(webhookUrl, secret);
                log.info("Sending notification to DingTalk: {}", messageBuilder.toString());
                String response = restTemplate.postForObject(finalUrl, request, String.class);
                log.info("DingTalk response: {}", response);
                
            } catch (Exception ex) {
                log.error("Failed to send notification to DingTalk", ex);
            }
        });
    }

    private String getPreviousStatus(InstanceEvent event) {
        if (event instanceof InstanceStatusChangedEvent) {
            return ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
        }
        return "UNKNOWN";
    }

    // 格式化字节数
    private String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        String pre = "KMGTPE".charAt(exp-1) + "";
        return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
    }

    private String getStatusEmoji(String status) {
        switch (status.toUpperCase()) {
            case "UP":
            case "CORRECT":
                return "🟢 服务正常";
            case "DOWN":
                return "🔴 服务下线";
            case "OFFLINE":
                return "⚫ 服务离线";
            case "BROKEN":
                return "🔥 服务故障";
            case "UNKNOWN":
                return "❓ 状态未知";
            case "RESTRICTED":
                return "⚠️ 服务受限";
            default:
                return "⚠️ " + status;
        }
    }

    private String getActionSuggestion(String status) {
        switch (status.toUpperCase()) {
            case "DOWN":
                return "> 1. 检查服务进程是否存活\n" +
                       "> 2. 查看服务日志获取错误信息\n" +
                       "> 3. 检查数据库、缓存等依赖服务是否正常\n" +
                       "> 4. 考虑重启服务\n";
            case "OFFLINE":
                return "> 1. 检查网络连接是否正常\n" +
                       "> 2. 确认服务器是否可访问\n" +
                       "> 3. 验证服务注册中心状态\n";
            case "BROKEN":
                return "> 1. 检查服务日志查找故障原因\n" +
                       "> 2. 验证关键组件状态\n" +
                       "> 3. 考虑回滚最近的变更\n";
            case "RESTRICTED":
                return "> 1. 检查服务限制条件\n" +
                       "> 2. 验证资源使用情况\n" +
                       "> 3. 考虑扩展服务容量\n";
            case "UNKNOWN":
                return "> 1. 检查监控客户端配置\n" +
                       "> 2. 验证服务健康检查接口\n" +
                       "> 3. 查看服务日志\n";
            case "UP":
            case "CORRECT":
                return "> - 服务运行正常，无需特别操作\n" +
                       "> - 建议定期检查服务日志和性能指标\n";
            default:
                return "> - 请检查服务日志获取详细信息\n" +
                       "> - 联系技术支持进行处理\n";
        }
    }

    private String addSignature(String webhookUrl, String secret) {
        try {
            long timestamp = System.currentTimeMillis();
            String stringToSign = timestamp + "\n" + secret;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            String sign = URLEncoder.encode(Base64.getEncoder().encodeToString(signData), "UTF-8");
            return webhookUrl + "&timestamp=" + timestamp + "&sign=" + sign;
        } catch (Exception e) {
            log.error("Error calculating signature", e);
            return webhookUrl;
        }
    }

    // 添加一个新的辅助方法来格式化运行时间
    private String formatUptime(long seconds) {
        long days = seconds / (24 * 3600);
        seconds %= (24 * 3600);
        long hours = seconds / 3600;
        seconds %= 3600;
        long minutes = seconds / 60;
        seconds %= 60;
        
        StringBuilder result = new StringBuilder();
        if (days > 0) result.append(days).append("天 ");
        if (hours > 0) result.append(hours).append("小时 ");
        if (minutes > 0) result.append(minutes).append("分钟 ");
        if (seconds > 0) result.append(seconds).append("秒");
        
        return result.toString();
    }
} 